package com.jzr.board.auth;

import com.jzr.board.db1.service.TUserService;
import com.jzr.board.db1.vo.MyUserDetailsVo;
import com.jzr.board.util.SpringUtil;
import com.jzr.common.util.U;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;

/**
 * Created by Shuangyao
 * 22:55 2018/10/15
 */
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    private Logger logger = LoggerFactory.getLogger(JwtAuthenticationFilter.class);

    @Autowired
    private JwtTokenProvider jwtTokenProvider;

    @Autowired
    private AuthParameters authParameters;

    @Autowired
    private TUserService userService;

    public JwtAuthenticationFilter(){
        authParameters = SpringUtil.getBean(AuthParameters.class);
        jwtTokenProvider = SpringUtil.getBean(JwtTokenProvider.class);
        userService = SpringUtil.getBean(TUserService.class);
    }

    //1.从每个请求header获取token
    //2.调用前面写的validateToken方法对token进行合法性验证
    //3.解析得到username，并从database取出用户相关信息权限
    //4.把用户信息以UserDetail形式放进SecurityContext以备整个请求过程使用。
    // （例如哪里需要判断用户权限是否足够时可以直接从SecurityContext取出去check
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        int code = HttpServletResponse.SC_FORBIDDEN;
        String msg = "无权访问";
        try {
            String userCode = null;
            String token = request.getHeader("token");
            if(token != null){ //普通token验证
                String[] tokens = token.split(" ");
                if(tokens == null || tokens.length<2){
                    code = HttpServletResponse.SC_FORBIDDEN;
                    msg = "token无效";
                    writeResult(request,response,code,msg);
                    return;
                }
                userCode = tokens[0];
                token = tokens[1];
                MyUserDetailsVo myUserDetailsVo = userService.getUserDetailByUserCode(userCode, 99);
                if (myUserDetailsVo == null) {
                    code = HttpServletResponse.SC_FORBIDDEN;
                    msg = "验证失败,apiKey不存在";
                    writeResult(request,response,code,msg);
                    return;
                }
                String token2 = myUserDetailsVo.getToken();
                if (U.isBlank(token2)) {
                    code = HttpServletResponse.SC_FORBIDDEN;
                    msg = "验证失败,token不存在";
                    writeResult(request,response,code,msg);
                    return;
                }else if (!token.equalsIgnoreCase(token2)) {
                    code = HttpServletResponse.SC_FORBIDDEN;
                    msg = "验证失败,token无效";
                    writeResult(request,response,code,msg);
                    return;
                }else {
                    Authentication authentication = new UsernamePasswordAuthenticationToken(
                            myUserDetailsVo, null, myUserDetailsVo.getAuthorities());
                    SecurityContextHolder.getContext().setAuthentication(authentication);
                    code = HttpServletResponse.SC_OK;
                }
            }else { //jwt验证
                token = getJwtFromRequest(request);
                if (token != null) {
                    boolean isTokenOk = false;
                    try {
                        isTokenOk = jwtTokenProvider.validateToken(token);
                    } catch (ExpiredJwtException e) {
                        code = HttpServletResponse.SC_EXPECTATION_FAILED;
                        msg = "token过期";
                        writeResult(request,response,code,msg);
                        return;
                    } catch (Exception e) {
                        e.printStackTrace();
                        code = HttpServletResponse.SC_EXPECTATION_FAILED;
                        msg = "token无效";
                        writeResult(request,response,code,msg);
                        return;
                    }

                    if (isTokenOk) {
                        Integer userId = getUserIdFromJwt(token, authParameters.getJwtTokenSecret());
                        if (userId != null) {
                            String userName = getUsernameFromJwt(token, authParameters.getJwtTokenSecret());
                            List<GrantedAuthority> authorities = getAuthoritiesFromJwt(token, authParameters.getJwtTokenSecret());
                            MyUserDetailsVo myUserDetailsVo = new MyUserDetailsVo(userId, userName, "", authorities);

                            Authentication authentication = new UsernamePasswordAuthenticationToken(
                                    myUserDetailsVo, null, myUserDetailsVo.getAuthorities());
                            SecurityContextHolder.getContext().setAuthentication(authentication);
                        }else {
                            code = HttpServletResponse.SC_EXPECTATION_FAILED;
                            msg = "token无效,无法获取userId";
                            writeResult(request,response,code,msg);
                            return;
                        }
                    }
                }
            }

            super.doFilter(request, response, filterChain);

        } catch (Exception e) {
            e.printStackTrace();
            code = HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
            msg = "系统错误,"+e.getMessage();
            writeResult(request,response,code,msg);
            return;
        }

        super.doFilter(request, response, filterChain);
    }

    /**
     * Get Bear jwt from request header Authorization.
     *
     * @param request servlet request.
     * @return token or null.
     */
    private String getJwtFromRequest(HttpServletRequest request) {
        String token = request.getHeader("Authorization");
        if(token == null) {
            token = request.getHeader("authorization");
        }
        if (token != null && token.startsWith("Bearer")) {
            return token.replace("Bearer ", "");
        }
        return null;
    }

    /**
     * Get user name from Jwt, the user name have set to jwt when generate token.
     *
     * @param token jwt token.
     * @param signKey jwt sign key, set in properties file.
     * @return user name.
     */
    private String getUsernameFromJwt(String token, String signKey) {
        return Jwts.parser().setSigningKey(signKey)
                .parseClaimsJws(token)
                .getBody()
                .getSubject();
    }

    private Integer getUserIdFromJwt(String token, String signKey) {
        String id = Jwts.parser().setSigningKey(signKey)
                .parseClaimsJws(token)
                .getBody()
                .getId();
        if(id == null || id.trim().length()<=0){
            return 0;
        }else {
            return Integer.parseInt(id);
        }
    }

    private List<GrantedAuthority> getAuthoritiesFromJwt(String token, String signKey) {
        String authoritiesString = Jwts.parser().setSigningKey(signKey)
                .parseClaimsJws(token)
                .getBody()
                .getAudience();
        return MyUserDetailsVo.GetAuthoritiesFromStr(authoritiesString);
    }

    private void writeResult(HttpServletRequest request, HttpServletResponse response,int code, String msg) throws IOException {
        SecurityContextHolder.getContext().setAuthentication(null);
        request.setAttribute("vStatus",code);
        request.setAttribute("vMessage",msg);
        response.setStatus(code);
        response.setContentType("application/json;charset=utf-8");
        PrintWriter out = response.getWriter();
        out.write("{\"code\":" + code + ",\"status\":" + code + ",\"msg\":\"" + msg + "\"" + ",\"message\":\"" + msg + "\"}");
        out.flush();
        out.close();
    }

}
